/**
 * 
 */
package com.hexacta.hrs.domain.feedback;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;

import org.vectrics.employee.Employee;

import com.hexacta.hrs.domain.feedback.motive.FeedbackMotiveProject;
import com.hexacta.hrs.domain.staffing.assignment.EmployeeAssignment;
import com.hexacta.hrs.repositories.EmployeeAssignmentRepository;
import com.hexacta.hrs.repositories.ProjectRepository;
import com.hexacta.hrs.repositories.employee.EmployeeRepository;
import com.hexacta.hrs.repositories.feedback.FeedbackMotiveRepository;
import com.hexacta.hrs.services.feedbacks.FeedbackService;
import com.hexacta.hrs.services.feedbacks.PeriodService;

/**
 * @author gvilla
 * 
 */
public class FeedbackPeriodFactory {

    private EmployeeRepository employeeRepository;

    private FeedbackService feedbackService;

    private FeedbackMotiveRepository feedbackMotiveRepository;

    private ProjectRepository projectRepository;

    private EmployeeAssignmentRepository employeeAssignmentRepository;

    private PeriodService periodService;

    private Period actualPeriod;

    public FeedbackPeriodFactory() {
    }

    /**
     * m�todo que itera sobre todos los empleados activos de la base, que
     * superenlos 90 d�as de antig�edad.
     * 
     */
    public void buildAll() {
        actualPeriod = new Period(Integer.toString(Calendar.getInstance().get(Calendar.YEAR)), Calendar.getInstance());
        periodService.save(actualPeriod);

        Collection<Employee> employees = employeeRepository.getActiveEmployees();

        for (Employee employee : employees) {
            createFeedbackPeriodFor(employee);
        }

    }

    /**
     * @param employee
     *            El siguiente metodo busca todos los evaluadores
     *            correspondientes al empleado, creando Feedbacks por cada uno
     *            de ellos, y asignandolos aun FeedbackPeriod
     */
    private void createFeedbackPeriodFor(final Employee employee) {

        // Se procede a buscar las asignaciones a proyectos, dentro de este
        // periodo
        Collection<EmployeeAssignment> assiggmentEmployee = employeeAssignmentRepository.getAssigmentsForThisPeriod(
                employee, actualPeriod.getYear());

        FeedbackPeriod period = new FeedbackPeriod(actualPeriod, employee.getFunction(),
                employee.getCareerRolePosition());
        period.setMentor(employee.getMentor());
        Collection<Employee> evaluators = new ArrayList<Employee>();
        for (EmployeeAssignment assigment : assiggmentEmployee) {

            // Busca los evaluadores dentro del proyecto al que fue asignado
            evaluators = findAllEvaluators(employee, assigment);

            for (Employee evaluator : evaluators) {
                buildFeedbackForAssignment(employee, period, assigment, evaluator);
            }

        }

        if (period.getFeedbacks().size() > 0) {
            employee.addFeedbackPeriod(period);
            employeeRepository.save(employee);
        }

    }

    /**
     * @param employee
     * @param assigment
     * @return
     */
    public Collection<Employee> findAllEvaluators(final Employee employee, final EmployeeAssignment assigment) {
        Collection<Employee> evaluators;
        evaluators = employeeRepository.getEvaluators(employee, actualPeriod.getYear(), assigment.getProject(),
                employee.getFunction().getWeight());

        Employee projectLeader = assigment.getProject().getEmployeeInCharge();
        if (!evaluators.contains(projectLeader) && employee.getFunction().getWeight() > 0
                && !employee.equals(projectLeader)) {
            evaluators.add(projectLeader);
        }

        Employee clientPartner = assigment.getProject().getClient().getEmployeeInCharge();
        if (!evaluators.contains(clientPartner) && employee.getFunction().getWeight() > 1
                && !employee.equals(clientPartner)) {
            evaluators.add(clientPartner);
        }
        return evaluators;
    }

    /**
     * @param employee
     * @param period
     * @param assigment
     * @param evaluator
     */
    public void buildFeedbackForAssignment(final Employee employee, final FeedbackPeriod period,
            final EmployeeAssignment assigment, final Employee evaluator) {
        Feedback feedback = new Feedback(actualPeriod.getYear(), employee, assigment.getAssignedRole(), evaluator);
        feedback.setCreationDay(Calendar.getInstance());
        Integer months = getMonthsForAssigment(assigment.getStartDate(), assigment.getEndDate());
        feedback.setMonthsInProject(months);

        FeedbackMotiveProject feedbackMotive = new FeedbackMotiveProject();
        feedbackMotive.setProject(assigment.getProject());

        feedback.setFeedbackMotive(feedbackMotive);

        period.addFeedback(feedback);

        feedbackMotiveRepository.save(feedbackMotive);
    }

    public Integer getMonthsForAssigment(final Calendar first, final Calendar second) {
        if (actualPeriod == null) {
            Period currentPeriod = periodService.getLastPeriod();
            setActualPeriod(currentPeriod);
        }
        Calendar endYear = GregorianCalendar.getInstance();
        endYear.setTime(new Date("12/31/" + actualPeriod.getYear()));
        Calendar firstDay = GregorianCalendar.getInstance();
        firstDay.setTime(new Date("01/01/" + actualPeriod.getYear()));

        Calendar begin = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        begin.setTime(first.getTime());
        end.setTime(second.getTime());

        if (!begin.after(firstDay) && !end.before(endYear))
            return 12;
        if (begin.after(firstDay) && end.before(endYear)) {
            end.add(GregorianCalendar.YEAR, -begin.get(GregorianCalendar.YEAR));
            end.add(GregorianCalendar.MONTH, -begin.get(GregorianCalendar.MONTH));
            end.add(GregorianCalendar.DAY_OF_MONTH, -begin.get(GregorianCalendar.DAY_OF_MONTH));

            return end.get(GregorianCalendar.MONTH);
        }
        if (begin.after(firstDay) && !end.before(endYear)) {
            Calendar lastDay = GregorianCalendar.getInstance();
            lastDay.setTime(endYear.getTime());
            lastDay.add(GregorianCalendar.YEAR, -begin.get(GregorianCalendar.YEAR));
            lastDay.add(GregorianCalendar.MONTH, -begin.get(GregorianCalendar.MONTH));
            lastDay.add(GregorianCalendar.DAY_OF_MONTH, -begin.get(GregorianCalendar.DAY_OF_MONTH));

            return endYear.get(GregorianCalendar.MONTH);
        }
        if (!begin.after(firstDay) && end.before(endYear)) {
            end.add(GregorianCalendar.YEAR, -firstDay.get(GregorianCalendar.YEAR));
            end.add(GregorianCalendar.MONTH, -firstDay.get(GregorianCalendar.MONTH));
            end.add(GregorianCalendar.DAY_OF_MONTH, -firstDay.get(GregorianCalendar.DAY_OF_MONTH));

            return end.get(GregorianCalendar.MONTH);
        }

        return null;
    }

    /**
     * @return the employeeRepository
     */
    public EmployeeRepository getEmployeeRepository() {
        return employeeRepository;
    }

    /**
     * @param employeeRepository
     *            the employeeRepository to set
     */
    public void setEmployeeRepository(final EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }

    /**
     * @return the projectRepository
     */
    public ProjectRepository getProjectRepository() {
        return projectRepository;
    }

    /**
     * @param projectRepository
     *            the projectRepository to set
     */
    public void setProjectRepository(final ProjectRepository projectRepository) {
        this.projectRepository = projectRepository;
    }

    /**
     * @return the employeeAssignmentRepository
     */
    public EmployeeAssignmentRepository getEmployeeAssignmentRepository() {
        return employeeAssignmentRepository;
    }

    /**
     * @param employeeAssignmentRepository
     *            the employeeAssignmentRepository to set
     */
    public void setEmployeeAssignmentRepository(final EmployeeAssignmentRepository employeeAssignmentRepository) {
        this.employeeAssignmentRepository = employeeAssignmentRepository;
    }

    public FeedbackMotiveRepository getFeedbackMotiveRepository() {
        return feedbackMotiveRepository;
    }

    public void setFeedbackMotiveRepository(final FeedbackMotiveRepository feedbackMotiveRepository) {
        this.feedbackMotiveRepository = feedbackMotiveRepository;
    }

    public FeedbackService getFeedbackService() {
        return feedbackService;
    }

    public void setFeedbackService(final FeedbackService feedbackService) {
        this.feedbackService = feedbackService;
    }

    public PeriodService getPeriodService() {
        return periodService;
    }

    public void setPeriodService(final PeriodService periodService) {
        this.periodService = periodService;
    }

    public Period getActualPeriod() {
        return actualPeriod;
    }

    public void setActualPeriod(final Period actualPeriod) {
        this.actualPeriod = actualPeriod;
    }

}
